package cn.xinfei.xdecision.common.service.enginex.impl;

import cn.xinfei.xdecision.common.model.datax.datainterface.v2.VariableVO;
import cn.xinfei.xdecision.common.model.datax.enums.VirtualDataSourceEnum;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.service.datax.datamanage.v2.impl.VariableServiceImpl;
import cn.xinfei.xdecision.common.service.enginex.util.NodeTag;
import cn.xinfei.xdecision.common.service.enginex.util.NodeUtil;
import cn.xinfei.xdecision.common.utils.constant.enginex.DecisionFlowCheckMessageConst;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.xinfei.xdecision.common.service.enginex.util.NodeUtil.*;
import static cn.xinfei.xdecision.common.utils.constant.enginex.DecisionFlowCheckMessageConst.*;
import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.DECISION_FLOW_CHECK_ERROR;
import static cn.xinfei.xdecision.common.utils.exception.util.ApiExceptionUtil.exception;

/**
 * 决策流部署上线前校验
 */
@Service
@Slf4j
public class DecisionFlowCheckService {
    @Resource
    private VariableServiceImpl variableService;
    @Resource
    private NodeJsonCheckService nodeJsonCheckService;
    @Resource
    private EngineNodeServiceImpl engineNodeService;

    /**
     * 决策流部署运行前安全性检查
     * <p>
     * 前置基础校验：
     * 1. 判断是否有孤立节点
     * 2. 判断是否有开始、结束节点
     * <p>
     * <p>
     * 查询决策流所有路径，对每条路径进行如下规则检查：
     * 1. 当前分支有开始有结束
     * 2. 分支节点到结束节点之间必须至少包含一个策略节点
     * 3. 并行节点到结束节点前必须包含聚合节点
     * 4. 聚合节点到开始节点间必须包含并行节点
     * 5. 并行与聚合节点之前不能有分支节点
     * 6. 挑战者分支上不允许再有【冠军挑战】节点
     * 7. 该分支有人行变量，则在首次使用人行变量的节点前，必须有人行节点配置
     * <p>
     * <p>
     * 节点属性配置完整性校验
     * 1. 规则库
     * 必须配置终止条件
     * 必须选择规则
     * 如果是串行，必须设置是否陪跑
     * <p>
     * 2. 评分卡、决策树、决策表必须选择组件中心的配置
     * 3. 分组、冠军挑战、并行都至少有两个条件组
     * 3. 分流 比例相加=100
     * 4. 子决策流必须配置子流id
     * 5. 决策选项
     * 决策条件、决策结果、默认结果、输出变量不能为空
     */
    public void decisionFlowPreOperationSafetyCheck(String engineCode, Long engineVersionId, List<EngineNode> nodes) {
        log.info("[决策流校验]引擎编码:{},引擎版本:{},节点列表信息:{}", engineCode, engineVersionId, JSON.toJSONString(nodes));
        if (!containsStartEndNode(nodes)) {
            throw exception(DECISION_FLOW_CHECK_ERROR, FLOW_CHECK_BRANCH_INCOMPLETE);
        }

        if (containsIsolatedNode(nodes)) {
            throw exception(DECISION_FLOW_CHECK_ERROR, BASE_RULE_CONTAINS_ISOLATED_NODE);
        }

        EngineNode startNode = nodes.stream().filter(node -> node.getNodeType().equals(NodeTypeEnum.START.getType())).findFirst().orElse(null);
        if (Objects.isNull(startNode)) {
            throw new ApiException(DECISION_FLOW_CHECK_ERROR.getCode(), DecisionFlowCheckMessageConst.BASE_RULE_START_END_NODE_MISSING);
        }
        Map<String, EngineNode> nodeMap = nodes.stream().collect(Collectors.toMap(EngineNode::getNodeCode, Function.identity()));
        List<List<EngineNode>> allPaths = allPaths(startNode, nodeMap);

        for (List<EngineNode> path : allPaths) {
            log.info("[决策流路径校验]路径节点信息:{}", JSON.toJSONString(path));
            if (!containsStartEndNode(path)) {
                throw exception(DECISION_FLOW_CHECK_ERROR, BASE_RULE_START_END_NODE_MISSING);
            }
            branchNodeCheck(path);
        }
        boolean b = allPaths.stream().flatMap(List::stream).anyMatch(node -> Objects.equals(node.getNodeType(), NodeTypeEnum.CREDIT_REPORT.getType())
                &&
                Objects.equals(node.getNodeTags(), NodeTag.CHALLENGER_TAG));
        if (b) {
            throw exception(DECISION_FLOW_CHECK_ERROR, FLOW_CHECK_RULE_CHAMPION_CHALLENGE_2);
        }

        nodes.forEach(node -> {
            log.info("[决策流节点属性配置完整性校验]节点信息:{}", JSON.toJSONString(node));
            nodeJsonCheckService.nodeJsonCheck(node);
        });
        //循环依赖校验
        NodeUtil.recursionEngines(engineCode, engineVersionId);
    }

    /**
     * 是否依赖人行变量
     */
    public boolean dependenceRhVar(EngineNode node) {
        String fieldCodes = node.getFieldCodes();
        if (StringUtil.isBlank(fieldCodes)) {
            return false;
        }
        List<String> fieldCodeList = Arrays.asList(fieldCodes.split(","));

        Collection<VariableVO> variableVOS = variableService.findVarTypeByVarCodes(fieldCodeList);
        if (variableVOS == null) {
            return false;
        }
        Map<String, VariableVO> variableTypeMap = variableVOS.stream().collect(Collectors.toMap(VariableVO::getType, v -> v, (k1, k2) -> k1));
        return variableTypeMap.get(VirtualDataSourceEnum.CREDIT_VAR.getCode()) != null;
    }

    public void branchNodeCheck(List<EngineNode> nodes) {
        boolean isChampionBranch = true;
        for (EngineNode node : nodes) {
            List<EngineNode> rightNodes = subNodesRight(node, nodes);
            List<EngineNode> leftNodes = subNodesLeft(node, nodes);

            log.info("currentNodeCode:{},rightNodes:{},leftNodes:{}", node.getNodeCode(), JSON.toJSONString(rightNodes), JSON.toJSONString(leftNodes));

            //如果是分支节点，必须得包含策略节点
            if (NodeUtil.isBranchNode(node.getNodeType()) && !containsStrategyNode(rightNodes)) {
                throw exception(DECISION_FLOW_CHECK_ERROR, FLOW_CHECK_RULE_BRANCH_NODE_HAVE_STRATEGY_NODE);
            }

            if (NodeTypeEnum.PARALLEL.getType().equals(node.getNodeType())) {
                if (!containsDesignatedTypeNode(rightNodes, NodeTypeEnum.AGGREGATION)) {
                    throw exception(DECISION_FLOW_CHECK_ERROR, FLOW_CHECK_RULE_PARALLEL_AGGREGATION_1);
                }
                //并行与聚合节点直接的节点list
                List<EngineNode> subList = subNodesDesignatedEndNodeType(node, nodes, NodeTypeEnum.AGGREGATION);
                log.info("并行与聚合间节点列表:{}", JSON.toJSONString(subList));
                if (!CollectionUtils.isEmpty(subList) && containsBranchNode(subList)) {
                    throw exception(DECISION_FLOW_CHECK_ERROR, FLOW_CHECK_RULE_PARALLEL_AGGREGATION_2);
                }
            }

            //如果是分支节点，必须得包含策略节点
            if (NodeTypeEnum.AGGREGATION.getType().equals(node.getNodeType()) && !containsDesignatedTypeNode(leftNodes, NodeTypeEnum.PARALLEL)) {
                throw exception(DECISION_FLOW_CHECK_ERROR, FLOW_CHECK_RULE_PARALLEL_AGGREGATION_3);
            }


            if (NodeTypeEnum.CHAMPION_CHALLENGE.getType().equals(node.getNodeType())) {
                String nodeJson = node.getNodeJson();
                JSONArray nodeJsonJ = JSON.parseArray(nodeJson);
                String championNextNode = "";
                for (int i = 0; i < nodeJsonJ.size(); i++) {
                    JSONObject branch = nodeJsonJ.getJSONObject(i);
                    Integer champion = branch.getInteger("champion");
                    if (champion != null && 1 == champion) {
                        championNextNode = branch.getString("nextNode");
                    }
                }
                //说明当前为挑战者分支
                Map<String, EngineNode> nodeMap = convertNodeList2MapNodeCodeKey(nodes);
                if (!nodeMap.containsKey(championNextNode)) {
                    isChampionBranch = false;
                    if (containsDesignatedTypeNode(rightNodes, NodeTypeEnum.CHAMPION_CHALLENGE)) {
                        throw exception(DECISION_FLOW_CHECK_ERROR, FLOW_CHECK_RULE_CHAMPION_CHALLENGE_1);
                    }
                }
            }

            //依赖人行变量节点前必须包含人行征信节点
            if (dependenceRhVar(node) && !containsDesignatedTypeNode(nodes, NodeTypeEnum.CREDIT_REPORT)) {
                throw exception(DECISION_FLOW_CHECK_ERROR, FLOW_CHECK_RULE_CREDIT_REPORT_1);
            }
        }

        //给当前分支节点打标记
        markBranchNode(nodes, isChampionBranch ? NodeTag.CHAMPION_TAG : NodeTag.CHALLENGER_TAG);
    }

    private void markBranchNode(List<EngineNode> nodes, int nodeTag) {
        //每一次都初始化，并重新判断是哪个分支
        nodes.forEach(node -> {
            node.setNodeTags(0);
            NodeTag.addTag(node, nodeTag);
        });
        engineNodeService.updateBatchById(nodes);
    }
}
